/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This is the default Validator base class. It uses the template pattern to
 * allow each extending class to implement their validation handlers. It
 * delegates the validation handler according to the real class instance of the
 * passed in value. It also provides default implementations for handling List
 * objects of type String, Number, and unknown classes as an Object.
 *
 * @author Anton Strack
 */
public abstract class Validator implements CsdwaValidator {

    /**
     * The name of the validator.
     */
    private String name;

    /**
     * Default constructor;
     */
    public Validator() {
        this.name = "";
    }

    /**
     * Default constructor with ability to set the name of the validator
     *
     * @param name the name of the validator object
     */
    public Validator(String name) {
        this.name = name;
    }

    /**
     * Get the generic error message for this validator.
     *
     * @param validatorValueDisplayName the name of the value to display to an
     * end user when there is an error. For instance Email for an email
     * ValidatorValue object.
     * @return The error message of the validator
     */
    @Override
    public String getErrorMessage(String validatorValueDisplayName) {
        return "";
    }

    /**
     * Check an object value if it is valid.
     *
     * @param value the value to validate against
     * @return true if the value is valid, false otherwise.
     */
    @Override
    public final boolean isValid(Object value) {
        boolean isValid;
        /**
         * The HttpServletRequest object stores the variables as an Object and
         * thus accept objects. However, the objects may be instances of String,
         * one of the Primitive wrappers extending Number, or a List<?> and thus
         * it is best to handle them as the class they actually are. If value
         * isn't one of the above, then handle it in the default object handler.
         */
        if (value instanceof java.lang.String) {
            isValid = this.handleString((String) value);
        } else if (value instanceof java.lang.Number) {
            isValid = this.handleNumber((Number) value);
        } else if (value instanceof java.util.List) {
            isValid = this.handleList((List<?>) value);
        } else {
            isValid = this.handleObject(value);
        }
        return isValid;
    }

    /**
     * @return the name
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * This method will validate objects that are instantiated as Strings.
     *
     * @param value the value to validate
     * @return true if the value is valid
     */
    protected abstract boolean handleString(String value);

    /**
     * This method will validate objects that are not Strings or Numbers.
     *
     * @param value the value to validate
     * @return true if the value is valid
     */
    protected abstract boolean handleObject(Object value);

    /**
     * This method will validate objects that are instantiated as a class that
     * extends java.lang.Number.
     *
     * @param value the value to validate
     * @return true if the value is valid
     */
    protected abstract boolean handleNumber(Number value);

    /**
     * This method handles the initial List object and delegates it's handling
     * to one of the specific list handlers.
     *
     * @param value The list value to handle
     * @return true if all the members of the list pass the validation test and
     * the list isn't empty, otherwise false.
     */
    protected final boolean handleList(List<?> value) {
        boolean isValid = false;
        List<?> col = (List) value;
        // check first that the List is empty. if so, then skip the if block and return false
        try {
            if (col.isEmpty() == false) {
                // if not empty then check the instance of the first value in the collection
                if (col.get(0) instanceof java.lang.String) {
                    //If we enter this block, the values of List should be Strings.
                    isValid = handleStringList((List<String>) col);
                } else if (col.get(0) instanceof java.lang.Number) {
                    //If we enter this block, the values of List should be Numbers.
                    isValid = handleNumberList((List<Number>) col);
                } else {
                    isValid = handleUnknownList((List<?>) col);
                }
            }
        } catch (ClassCastException ex) {
            Logger.getLogger(Validator.class.getName()).log(Level.SEVERE, "The List object provided, contains inproper object class types:" + value.toString(), ex);
            return false;
        } catch (Exception ex) {
            Logger.getLogger(Validator.class.getName()).log(Level.SEVERE, "Exception caught in handleList:" + value.toString(), ex);
            return false;
        }
        return isValid;
    }

    /**
     * This method handles a List of Strings by iterating trough them and
     * passing each item to the handleString() method.
     *
     * @param values the list of Strings
     * @return true if every element of the list passes the validation test.
     */
    protected boolean handleStringList(List<String> values) {
        boolean isValid = true;
        int i = 0;
        int count = values.size();
        while (isValid == true && i < count) {
            isValid = this.handleString(values.get(i));
            i++;
        }
        return isValid;
    }

    /**
     * This method handles a List of Numbers by iterating trough them and
     * passing each item to the handleNumber() method.
     *
     * @param values the list of Numbers
     * @return true if every element of the list passes the validation test.
     */
    protected boolean handleNumberList(List<Number> values) {
        boolean isValid = true;
        int i = 0;
        int count = values.size();
        while (isValid == true && i < count) {
            isValid = this.handleNumber(values.get(i));
            i++;
        }
        return isValid;
    }

    /**
     * This method handles a List of Unknown by iterating trough them and
     * passing each item to the handleObject() method.
     *
     * @param values the list of Unknowns
     * @return true if every element of the list passes the validation test.
     */
    protected boolean handleUnknownList(List<?> values) {
        boolean isValid = true;
        int i = 0;
        int count = values.size();
        while (isValid == true && i < count) {
            isValid = this.handleObject(values.get(i));
            i++;
        }
        return isValid;
    }
}
